package com.xl.studentpass.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xl.studentpass.entity.CommentEntity;
import com.xl.studentpass.entity.CommunityEntity;
import com.xl.studentpass.entity.UserEntity;
import com.xl.studentpass.entity.po.PostingDetailPo;
import com.xl.studentpass.entity.po.PostingInfoPo;
import com.xl.studentpass.entity.vo.CommunityVo;
import com.xl.studentpass.mapper.CommentMapper;
import com.xl.studentpass.mapper.CommunityMapper;
import com.xl.studentpass.mapper.ImageMapper;
import com.xl.studentpass.mapper.UserMapper;
import com.xl.studentpass.service.CommunityService;
import com.xl.studentpass.utils.ImageIdType;
import com.xl.studentpass.utils.LocalDateUtils;
import com.xl.studentpass.utils.ThreadPoolUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.xl.studentpass.service.impl.AcceptTaskServiceImpl.getStrings;
import static com.xl.studentpass.utils.ImageIdType.COMMUNITY;
import static com.xl.studentpass.utils.ImageIdType.USER;
import static com.xl.studentpass.utils.TaskOrderStatus.FAVORITE;
import static com.xl.studentpass.utils.TaskOrderStatus.LIKE;

/**
 * @author: xl
 * @createTime: 2023/5/31 15:12:12
 * @program: student-pass
 * @e-mail: 2199396150@qq.com
 * @description:
 */
@Service
@Slf4j
public class CommunityServiceImpl extends ServiceImpl<CommunityMapper, CommunityEntity> implements CommunityService {
    //@Resource
    ThreadPoolExecutor threadPoolExecutor = ThreadPoolUtils.getThreadPool("community");
    @Resource
    ImageMapper imageMapper;

    @Resource
    UserMapper userMapper;

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Resource
    CommentMapper commentMapper;

    private final String NAME = this.getClass().getName();


    /**
     * 发布帖子
     */
    @Override
    public Integer releasePosting(CommunityVo communityVo) {
        if (!communityVo.getUserId().equals(StpUtil.getLoginIdAsInt())) {
            log.error(NAME + "用户信息与登录信息不符");
            return -1;
        }
        if (ObjectUtils.isEmpty(communityVo)) {
            log.error(NAME + "出入数据为空");
            return -1;
        }
        CommunityEntity community = new CommunityEntity();
        BeanUtil.copyProperties(communityVo, community);
        String nowDate = LocalDateUtils.getLocalDateTimeStr();
        community.setCreateTime(nowDate);
        community.setOperaTime(nowDate);
        boolean b = this.saveOrUpdate(community);
        if (!b) {
            log.error(NAME + "数据库插入失败");
            return -1;
        }
        return 1;
    }

    /**
     * 删除帖子
     */
    @Override
    public Integer deletePosting(Integer userId, Integer communityId) {
        if (ObjectUtils.isEmpty(userId) || !userId.equals(StpUtil.getLoginIdAsInt())) {
            log.error(NAME + "用户信息与登录信息不符");
            return -1;
        }
        if (ObjectUtils.isEmpty(communityId)) {
            log.error(NAME + "出入数据为空");
            return -1;
        }
        boolean b = this.removeById(communityId);
        if (!b) {
            log.error(NAME + "数据库删除失败");
            return -1;
        }
        return 1;
    }

    /**
     * 修改帖子
     */
    @Override
    public Integer alterPosting(CommunityVo communityVo) {
        if (!communityVo.getUserId().equals(StpUtil.getLoginIdAsInt())) {
            log.error(NAME + "用户信息与登录信息不符");
            return -1;
        }
        if (ObjectUtils.isEmpty(communityVo)) {
            log.error(NAME + "出入数据为空");
            return -1;
        }
        CommunityEntity community = new CommunityEntity();
        BeanUtil.copyProperties(communityVo, community);
        community.setOperaTime(LocalDateUtils.getLocalDateTimeStr());
        boolean b = this.updateById(community);
        if (!b) {
            log.error(NAME + "数据库修改失败");
            return -1;
        }
        return 1;
    }

    /**
     * 获取帖子详情
     */
    @Override
    public PostingDetailPo getPostingDetailById(Integer communityId) {
        CommunityEntity community = this.getById(communityId);

        log.info(NAME + "community===============" + community);

        List<PostingInfoPo> postingInfoPos = getPostingInfoPo(Collections.singletonList(community));

        log.info(NAME + "postInfo================" + Arrays.toString(postingInfoPos.toArray()));

        PostingDetailPo postingDetailPo = new PostingDetailPo();
        BeanUtils.copyProperties(postingInfoPos.get(0), postingDetailPo);

        postingDetailPo.setComments(getComment(communityId));
        log.info(NAME + "detail============" + postingDetailPo);
        return postingDetailPo;
    }

    /**
     * 获取全部帖子
     */
    @Override
    public List<PostingInfoPo> getList(Integer pageNo, Integer pageSize) {
        if (ObjectUtils.isEmpty(pageNo) || ObjectUtils.isEmpty(pageSize)) {
            pageNo = 1;
            pageSize = 10;
        }
        pageNo = (pageNo - 1) * pageSize;
        Page<CommunityEntity> page = new Page<>(pageNo, pageSize);

        List<CommunityEntity> communities = this.baseMapper.selectPage(page, null).getRecords();

        return getPostingInfoPo(communities);

    }

    @Override
    public List<PostingInfoPo> getPostingInfoPo(List<CommunityEntity> communities) {
        ArrayList<PostingInfoPo> postingInfoPos = new ArrayList<>();

        //循环遍历异步请求为每一个帖子添加用户等信息
        communities.forEach(x -> {
            PostingInfoPo postingInfoPo = new PostingInfoPo();
            BeanUtil.copyProperties(x, postingInfoPo);


            //获取图片地址
            CompletableFuture<List<String>> imageUrlsCompletableFuture = CompletableFuture.supplyAsync(() -> {
                List<String> imageUrls = getImageUrls(x.getId(), COMMUNITY);
                postingInfoPo.setImageUrls(imageUrls);
                return imageUrls;
            }, threadPoolExecutor);

            //获取点赞
            CompletableFuture<Integer> likesCompletableFuture = CompletableFuture.supplyAsync(() -> {

                int likes = getLikeOrFavoriteCount(x, LIKE.getStatusName());
                postingInfoPo.setLikeCount(likes);

                Integer userId = null;
                try {
                    userId = StpUtil.getLoginIdAsInt();
                } catch (Exception ignored) {
                }

                //判断当前用户是否点赞了
                if (!ObjectUtils.isEmpty(userId)) {
                    Boolean checkLike = checkLikeOrFavorite(userId, LIKE.getStatusName());
                    if (!ObjectUtils.isEmpty(checkLike)) {
                        postingInfoPo.setHasLike(checkLike);
                    } else {
                        postingInfoPo.setHasLike(false);
                    }
                }

                return likes;
            }, threadPoolExecutor);

            //获取收藏数
            CompletableFuture<Integer> favoritesCompletableFuture = CompletableFuture.supplyAsync(() -> {
                int isFavorites = getLikeOrFavoriteCount(x, FAVORITE.getStatusName());
                int i = ObjectUtils.isEmpty(isFavorites) ? 0 : isFavorites;
                postingInfoPo.setFavoriteCount(i);

                Integer userId = null;
                try {
                    userId = StpUtil.getLoginIdAsInt();
                } catch (Exception ignored) {
                }

                //判断当前用户是否点赞
                if (!ObjectUtils.isEmpty(userId)) {
                    Boolean checkFavorite = checkLikeOrFavorite(userId, FAVORITE.getStatusName());
                    if (!ObjectUtils.isEmpty(checkFavorite)) {
                        postingInfoPo.setHasFavorite(checkFavorite);
                    }else {
                        postingInfoPo.setHasFavorite(false);
                    }
                }
                return i;
            }, threadPoolExecutor);

            //获取评论数
            CompletableFuture<Integer> commentCompletableFuture = CompletableFuture.supplyAsync(() -> {
                int commentCount = getCommentCount(x);
                postingInfoPo.setCommentCount(commentCount);
                return commentCount;
            }, threadPoolExecutor);

            //获取用户信息
            CompletableFuture<UserEntity> userCompletableFuture = CompletableFuture.supplyAsync(() -> {
                UserEntity userById = getUserById(x.getUserId());
                postingInfoPo.setUserName(userById.getUserName());
                postingInfoPo.setUserId(userById.getId());
                postingInfoPo.setHeaderUrl(userById.getImageUrl());
                return userById;
            }, threadPoolExecutor);

            CompletableFuture.allOf(
                    imageUrlsCompletableFuture,
                    likesCompletableFuture,
                    favoritesCompletableFuture,
                    commentCompletableFuture,
                    userCompletableFuture
            ).join();
            postingInfoPos.add(postingInfoPo);
        });

        return postingInfoPos;
    }

    /**
     * 判断用户是否点赞
     */
    private Boolean checkLikeOrFavorite(Integer userId, String type) {
        String key = "userId" + userId;
        Set<Object> keys = stringRedisTemplate.opsForHash().keys(key);
        for (Object o : keys) {
            String hashKey = (String) o;
            String[] fields = hashKey.split(":");
            if (ObjectUtils.isEmpty(fields) && fields.length <= 0) {
                return false;
            }
            return fields[0].contains(type);
        }
        return false;
    }

    /**
     * 点赞数or收藏数
     */
    private int getLikeOrFavoriteCount(CommunityEntity x, String likeType) {
        String community = "community:" + x.getId();
        Set<Object> keys = stringRedisTemplate.opsForHash().keys(community);
        int count = 0;
        for (Object k : keys) {
            if (String.valueOf(k).startsWith(likeType)) {
                count += 1;
            }
        }
        return count;
    }

    /**
     * 评论数
     */
    private int getCommentCount(CommunityEntity x) {
        QueryWrapper<CommentEntity> wrapper = new QueryWrapper<>();
        wrapper.groupBy(COMMUNITY.getName());
        wrapper.eq(COMMUNITY.getName(), x.getId());
        Integer count = commentMapper.selectCount(wrapper);
        if (ObjectUtils.isEmpty(count)) {
            return 0;
        }
        return count;
    }

    private UserEntity getUserById(Integer userId) {
        UserEntity user = userMapper.selectById(userId);
        List<String> imageUrls = getImageUrls(userId, USER);
        if (!ObjectUtils.isEmpty(imageUrls) && imageUrls.size() > 0) {
            user.setImageUrl(imageUrls.get(0));
        }
        return user;
    }

    /**
     * 添加评论属性
     */
    private List<CommentEntity> getComment(Integer communityId) {

        LambdaQueryWrapper<CommentEntity> wrapper = new LambdaQueryWrapper<>();
        //获取一条帖子的所有评论
        wrapper.eq(CommentEntity::getCommunityId, communityId);
        List<CommentEntity> comments = commentMapper.selectList(wrapper);
        return comments.stream()
                .filter(x -> x.getValid() != 1)
                .peek(x -> {
                    x.setUserName(userMapper.selectById(x.getUserId()).getUserName());
                    List<String> imageUrls = getImageUrls(x.getUserId(), USER);
                    if (!ObjectUtils.isEmpty(imageUrls) && imageUrls.size() > 0) {
                        x.setHeadUrl(imageUrls.get(0));
                    }
                })
                .peek(x -> x.setChildren(selectChildren(comments, x)))
                .collect(Collectors.toList());
    }

    /**
     * 递归设置评论的子评论
     */
    private List<CommentEntity> selectChildren(List<CommentEntity> comments, CommentEntity root) {
        return comments.stream()
                .filter(comment -> comment.getParentId().equals(root.getId()))
                .peek(x -> x.setChildren(selectChildren(comments, x)))
                .collect(Collectors.toList());
    }


    private List<String> getImageUrls(Integer id, ImageIdType idType) {
        return getStrings(id, imageMapper, idType);
    }
}
